package com.qiuling.iot.service.impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.qiuling.iot.Exception.BusinessException;
import com.qiuling.iot.Exception.code.BaseResponseCode;
import com.qiuling.iot.config.RabbitConfig;
import com.qiuling.iot.constants.Constant;
import com.qiuling.iot.entity.EquipmentData;
import com.qiuling.iot.entity.EquipmentInfo;
import com.qiuling.iot.entity.SysUser;
import com.qiuling.iot.mapper.EquipmentInfoMapper;
import com.qiuling.iot.mapper.SysUserMapper;
import com.qiuling.iot.service.EquipmentInfoService;
import com.qiuling.iot.utils.CodeUtil;
import com.qiuling.iot.utils.RedisUtils;
import com.qiuling.iot.utils.SMSUtils;
import com.qiuling.iot.vo.req.EquipmentInfoAddReqVO;
import com.qiuling.iot.vo.req.EquipmentInfoChangeReqVO;
import com.qiuling.iot.vo.req.EquipmentInfoUpdateReqVO;
import com.qiuling.iot.vo.resp.EquipmentInfoRespNodeVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * @Author：zhangqiang
 * @Description：
 * @Date：Created in 0:48 2020/2/13
 * @Modified By：
 */
@Service
@Slf4j
@Transactional(readOnly = true)
public class EquipmentInfoServiceImpl implements EquipmentInfoService {

    @Autowired
    private EquipmentInfoMapper equipmentInfoMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ObjectMapper objectMapper;



    @Override
    public List<EquipmentInfo> selectAll(String userId) {
        List<EquipmentInfo> list = equipmentInfoMapper.selectAll(userId);
        list.forEach(s -> {
            EquipmentInfo parent = equipmentInfoMapper.selectByPrimaryKey(s.getPid());
            if (parent != null) {
                s.setPidName(parent.getEquipmentName());
            }
        });
        return list;
    }

    @Override
    public List<EquipmentInfoRespNodeVO> EquipmentTreeList(String equipmentId, String userId) {
        List<EquipmentInfo> list = equipmentInfoMapper.selectAll(userId);
        if (!StringUtils.isEmpty(equipmentId) && !list.isEmpty()) {
            for (EquipmentInfo e :
                    list) {
                if (e.getId().equals(equipmentId)) {
                    list.remove(e);
                    break;
                }
            }
        }
        EquipmentInfoRespNodeVO respNodeVO = new EquipmentInfoRespNodeVO();
        respNodeVO.setId("0");
        respNodeVO.setTitle("默认顶级设备");
        respNodeVO.setChildren(getTree(list));
        List<EquipmentInfoRespNodeVO> result = Lists.newArrayList();
        result.add(respNodeVO);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public EquipmentInfo addEquipmentInfo(EquipmentInfoAddReqVO vo, String userId) {
        String relationCode;
        long equipmentCount = redisUtils.incrby(Constant.EQUIPMENT_CODE_KEY, 1);
        String equipmentCode = CodeUtil.getCode(String.valueOf(equipmentCount), 7, "0");
        EquipmentInfo parent = equipmentInfoMapper.selectByPrimaryKey(vo.getPid());
        if (vo.getPid().equals("0")) {
            relationCode = equipmentCode;
        } else if (null == parent) {
            log.info("父级设备不存在{}", vo.getPid());
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        } else {
            relationCode = parent.getRelationCode() + equipmentCode;
        }
        EquipmentInfo equipmentInfo = new EquipmentInfo();
        BeanUtils.copyProperties(vo, equipmentInfo);
        equipmentInfo.setId(UUID.randomUUID().toString());
        equipmentInfo.setCreateTime(new Date());
        equipmentInfo.setEquipmentCode(equipmentCode);
        equipmentInfo.setRelationCode(relationCode);
        equipmentInfo.setUserId(userId);
        int i = equipmentInfoMapper.insertSelective(equipmentInfo);
        if (i != 1) {
            throw new BusinessException(BaseResponseCode.OPERATION_ERROR);
        }
        return equipmentInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateEquipmentInfo(EquipmentInfoUpdateReqVO vo) {
        //保存更新后的数据
        EquipmentInfo equipmentInfo = equipmentInfoMapper.selectByPrimaryKey(vo.getId());
        if (null == equipmentInfo) {
            log.error("传入的id：{}不合法", vo.getId());
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        }
        EquipmentInfo update = new EquipmentInfo();
        BeanUtils.copyProperties(vo, update);
        int count = equipmentInfoMapper.updateByPrimaryKeySelective(update);
        if (count != 1) {
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        }
        //维护层级关系
        if (vo.getPid().equals(equipmentInfo.getPid())) {
            //子集的部门层级关系编码=父级部门层级关系编码+它本身部门编码
            EquipmentInfo newParent = equipmentInfoMapper.selectByPrimaryKey(vo.getPid());
            if (!vo.getPid().equals("0") && null == newParent) {
                log.info("修改后的设备在数据中查不到：{}", vo.getPid());
                throw new BusinessException(BaseResponseCode.DATA_ERROR);
            }
            EquipmentInfo oldParent = equipmentInfoMapper.selectByPrimaryKey(equipmentInfo.getPid());
            String oldRelation;
            String newRelation;
            if (equipmentInfo.getPid().equals("0")) {
                oldRelation = equipmentInfo.getRelationCode();
                newRelation = newParent.getRelationCode() + equipmentInfo.getEquipmentCode();

            } else if (vo.getPid().equals("0")) {
                oldRelation = equipmentInfo.getRelationCode();
                newRelation = equipmentInfo.getEquipmentCode();
            } else {
                oldRelation = oldParent.getRelationCode();
                newRelation = newParent.getRelationCode();
            }
            equipmentInfoMapper.updateRelationCode(oldRelation, newRelation, equipmentInfo.getRelationCode());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteEquipmentInfo(String id) {
        EquipmentInfo equipmentInfo = equipmentInfoMapper.selectByPrimaryKey(id);
        if (equipmentInfo == null) {
            log.error("传入的id在数据库中找不到：{}", id);
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        }
        List<String> list = equipmentInfoMapper.selectChildIds(equipmentInfo.getRelationCode());
        //判断它和它自己是否包含其他的可控设备
        List<EquipmentInfo> equipmentInfos = equipmentInfoMapper.selectEqupmentByIds(list);
        //如果大于1，说明下面关联其他设备，等于1时说明只有它自己，可以删除
        if (equipmentInfos.size() > 1) {
            throw new BusinessException(BaseResponseCode.NOT_PERMISSION_DELETED_EQUPMENT);
        }
        //逻辑删除设备数据
        int count = equipmentInfoMapper.deleteEquipments(list);
        if (count == 0) {
            throw new BusinessException(BaseResponseCode.OPERATION_ERROR);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeStatus(EquipmentInfoChangeReqVO vo, String userId) {
        EquipmentInfo equipmentInfo = new EquipmentInfo();
        BeanUtils.copyProperties(vo, equipmentInfo);

        /**
         * 1.开启线程池监控，等待客户端连接
         * 2.客户端连接后验证心跳包
         * 3.发送命令码
         * 4.关闭连接（接收的信息由别的线程处理）
         */
        //验证操作目标设备归属
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(userId);
        if(sysUser != null) {
            EquipmentInfo targetEquip = equipmentInfoMapper.getEquipmentInfoByUserIdAndEquiptmentId(vo.getId(), sysUser.getId());
            if(targetEquip == null) {
                throw new BusinessException(BaseResponseCode.EQUIPMENT_NOT_EXIST);
            }
            EquipmentInfo parentEquip = equipmentInfoMapper.selectByPrimaryKey(targetEquip.getPid());
            log.info(String.format("客户端设备：%s开始连接，编码：%s", parentEquip.getEquipmentName(), parentEquip.getEquipmentCode()));
            //TODO 由于环境限制，无第三放接口调用，这里只发送信息
            Integer opened = equipmentInfo.getOpened();
            EquipmentData equipmentData = new EquipmentData();
            if(opened == 1) {
                equipmentData.setSendData("0xff");
            } else {
                equipmentData.setSendData("0x00");
            }
            equipmentData.setEquipmentCode(targetEquip.getEquipmentCode());
            equipmentData.setEquipmentName(targetEquip.getEquipmentName());
            equipmentData.setId(UUID.randomUUID().toString());
            String dataStr = JSON.toJSONString(equipmentData);

            try {
                rabbitTemplate.setExchange(RabbitConfig.IOT_EXCHANGE);
                rabbitTemplate.setRoutingKey(RabbitConfig.DATA_ROUTING_KEY);
                rabbitTemplate.convertAndSend(MessageBuilder.withBody(objectMapper.writeValueAsBytes(dataStr)).build());
                /**
                 * 向用户发送信息，由于阿里云不支持未上线APP发送信息，这里值发送验证码
                 * 测试用，验证码写死，请无视短信内容
                 */
                SMSUtils.sendSMS(sysUser.getPhone(), "258468");
            } catch (JsonProcessingException e) {
                log.error("发送异常:{}", e.getMessage());
            }
        }

        /**
         * 更改设备状态
         */
        int count = equipmentInfoMapper.updateByPrimaryKeySelective(equipmentInfo);
        if (count == 0) {
            throw new BusinessException(BaseResponseCode.OPERATION_ERROR);
        }

    }

    @Override
    public EquipmentInfo getEquipmentInfoByIdAndUserId(String id, String userId) {
        EquipmentInfo equip = equipmentInfoMapper.getEquipmentInfoByUserIdAndEquiptmentId(id, userId);
        return equip;
    }

    private List<EquipmentInfoRespNodeVO> getTree(List<EquipmentInfo> all) {
        List<EquipmentInfoRespNodeVO> list = Lists.newArrayList();
        all.forEach(e -> {
            if (e.getPid().equals("0")) {
                iteratorList(all, list, e);
            }
        });
        return list;
    }


    private void iteratorList(List<EquipmentInfo> all, List<EquipmentInfoRespNodeVO> list, EquipmentInfo e) {
        EquipmentInfoRespNodeVO respNodeVO = new EquipmentInfoRespNodeVO();
        respNodeVO.setId(e.getId());
        respNodeVO.setTitle(e.getEquipmentName());
        respNodeVO.setChildren(getChild(e.getId(), all));
        list.add(respNodeVO);
    }

    private List<EquipmentInfoRespNodeVO> getChild(String id, List<EquipmentInfo> all) {
        List<EquipmentInfoRespNodeVO> list = Lists.newArrayList();
        all.forEach(e -> {
            if (e.getPid().equals(id)) {
                iteratorList(all, list, e);
            }
        });
        return list;
    }
}
